(function(rules,globalObj){// borrowed from prototypejs

Object.extend = function(destination, source) {
	for (var property in source){
		destination[property] = source[property];
	}
	return destination;
};

// By D. Crockford

Object.create = function( o){
	var F = function(){};
	F.prototype=o;
	return new F();
};var algors = {
	"Naked Single-": function(){
			var data = sdkHistory.current();
			var singles= data.filterByCount(1),
				houseIDs= rules.houseIDs,
				i=0,
				j=0,
				k=0,
				found = false,
				neighbours = [];
				
			do{
				// Iterating Singles
				j=0;
				do{
					//Iterating Houses
					if( singles[i].isInHouse( houseIDs[j])){
						neighbours= data.filterByHouse( houseIDs[j]).filterNotCount(1);
						k=0;
						do{
							// Iterating Neighbours
							found = neighbours[k].disable( singles[i].candids[0]);
						}while( !found && ++k< neighbours.length);
					}
				}while( !found && ++j< houseIDs.length);
			}while( !found && ++i< singles.length);
			if( found){
				return found;
			}
		},
	"Naked Single": function( ){
			var data = sdkHistory.current(),
				changes = sdkChange([]);
			data.filterByCount(1).forEach( function(solvedCell){
				solvedCell.houses.forEach( function(sCHouseId){
					changes = changes.add( data.filterByHouse( sCHouseId).filterNotCount(1).disable( solvedCell.candids[0]));
				});
			});
			changes = sdkChange.unique( changes);
			return changes;
		},
	"Hidden Single": function( data){
			var data = sdkHistory.current(),
				changes = sdkChange([]),
				blub;
			rules.houseIDs.forEach( function(house){
				rules.candidIDs.forEach( function(candidID){
					blub = data.filterByHouse( house).filterByCandid( candidID);
					if( blub.length === 1 && blub[0].candids.length !== 1){ 
						changes = changes.add(blub.only( candidID));
					}
				});
			});
			
			changes = sdkChange.unique( changes);
			return changes;
		}
	};
(function(){
	
globalObj.sdkCell = function sdkCellFn( id, candids, houses){
	var p = Object.create(sdkCell.proto);
	var o= {
		candids: candids,
		houses: houses,
		get id(){ return id}
	};
	p=Object.extend(p,o);
	return p;
};

globalObj.sdkCell.proto = {
		contains: function ( candid) {
			return this.candids.indexOf(candid)>=0;
		},
		isSolved: function (){ 
			return this.candids.length === 1;
		},
		enable: function( candidID, bool){
			if( bool && !this.contains( candidID)){
				this.candids.push( candidID);
				return this;
			}
			if( !bool && !this.contains( candidID)){
				return sdkChange("enable", this.id, candidID);
			}
		},
		disable: function( candidID, bool){
			if( bool && this.contains(candidID)){
				this.candids= this.candids.filter(function( elem){
					return elem !== candidID;
				});
				return this;
			}
			if( !bool && this.contains(candidID)){
				return sdkChange("disable", this.id, candidID);
			}
		},
		only: function( candidID, bool){
			if( bool){
				this.candids = ( this.candids.length === 1) ? rules.candidIDs: [candidID];
				return this;
			} else {
				return sdkChange("only", this.id, candidID);
			}
		},
		swap: function ( candidID, bool){	
			if( bool){
				if( this.contains( candidID)){
					this.disable( candidID, bool);
				}else{
					this.enable( candidID, bool);
				}
				return this;
			} else {
				return sdkChange("swap", this.id, candidID);
			}
		},
		isInHouse: function( houseId){
			return this.houses.indexOf( houseId ) >=0;
		},
		count: function(){
			return this.candids.length;
		}
};

globalObj.sdkCell.copy= function( cell){
	return sdkCell( cell.id, cell.candids.concat(), cell.houses.concat() );
};

})();

function sdkChangeSingle( cmd, cellID, candidID){
	return {
		cmd: cmd,
		cellID:cellID,
		candidID: candidID,
		exec: function( slice){
			slice=sdkSlice( slice.concat());
			var a = slice.filterByID( cellID);
			switch( cmd){
				case "swap":
					a=a.swap( candidID, true);
					break;
				case "only":
					a=a.only( candidID, true);
					break;
				case "disable":
					a=a.disable( candidID, true);
					break;
				case "enable":
					a=a.enable( candidID, true);
					break;
				default:
					throw "Unknown command for sdkChange: "+ cmd;
			}
			a=a.concat( slice.filterNotID(cellID));
			return sdkSlice(a);
		},
		add: function( arr){
			return [this, arr];
		},
		id : function(){
			return cmd+cellID+candidID;
		}
	};
}

var sdkChange = Object.extend(function sdkChangeFn( arg){
	if( arguments.length > 1){
		return sdkChangeSingle.apply(undefined, arguments);
	} else {
		if( !arg){
			arg = [];
		}
		arg.exec = function( arr){
			arr = arr.concat();
			this.forEach(function( elem){
				arr= elem.exec(arr);
			});
			return arr;
		};
		arg.add = function( arr){
			return sdkChange( this.concat( arr));
		};

		return arg;
	}
},{
	unique: function( arr){
		var newArr= [],
			ids= {};
			
		for( var i=0; i< arr.length; i++){
			if( arr[i] !== undefined && !ids[ arr[i].id()] ){
				newArr.push( arr[i]);
				ids[ arr[i].id()]=true;
			}
		}
		
		return sdkChange( newArr);
	}
});
var sdkHistory = {
	history: [],
	top: [],
	bottom: [],
	step: 0,
	current: function(){
		return this.top;
	},
	next: function( step){
		if( step){
			this.history.unshift( step);
			this.top = step.exec( this.top);
			return this.top;
		}
	},
	base: function( arg){
		this.bottom = arg;
		this.history= [];
		this.top= sdkSlice.copy(arg);
		this.arg= 0;
	}
};

var intercom = {
	handler: [],
	register: function(cmd, callback, context){
		this.handler.push( {"cmd":cmd,"callback":callback,"context":context});
	},
	catcher : function( e){
		var ret = {},
			fn = this.handler.filter( function( handle){
				return handle.cmd === e.data.cmd;
			},this);
		
		if( fn.length ===0){
			return "fail.";
		}
		if( fn.length >1){
			return "too many handlers";
		}
		fn=fn[0];
		try{
			ret = fn.callback.call(fn.context,e.data);
		}catch(error){
			cl(e.data.cmd);
			
			throw(error);
		}
		if(ret===undefined){
			ret = {cmd:"void"};
		}
		if(!ret.refid){
			ret.refid = e.data.refid;
		}
		postMessage( ret);
	}
};

function cl( a){
	postMessage({cmd:"cl",args:{value:a}});
}

function tempCatcher(e){
	intercom.catcher(e);
}
globalObj.onmessage = tempCatcher;

intercom.register("init", function(e){
	sdkHistory.base( sdkSlice(e.args.field));
	return getHandler({args:{value:"field"}});
});

function getHandler(e){
	switch( e.args.value){
		case "field":
			return {
				cmd: "post_field",
				args: {
					value: sdkHistory.current()
				}
			};
		case "cell":
			return {
				cmd: "post_cell",
				args: {
					value: sdkHistory.current().filterByID(e.args.cellID)
				}
			};
		case "all_candids":
			return {
				cmd: "post_all_candids",
				args:{
					value: rules.candidIDs
				}
			};
		default:
			if(rules[e.args.value]){
				return {
					cmd: "post_"+e.args.value,
					args:{
						value: rules[e.args.value]
					}
				};
			}
			return undefined;
	}
}
intercom.register("get", getHandler);

intercom.register("get_multiple",function(e){
	var ret = {};
	for( var i=0; i<e.args.values.length;i++){
		var req= e.args.values[i];
		ret[req]=getHandler({args:{value:req}}).args.value;
	}
	return {cmd:"post_multiple",args:ret};
});
intercom.register("status",function(e){
	return {
		cmd:"cl",
		args:{
			value:sdkHistory
		}
	};
});
intercom.register("change",function(e){
switch( e.args.value){
	case "swap":
		//history.unshift( sdkChange("swap",e.args.cellID,e.args.candidID));
		//field = history[0].exec( field);
		sdkHistory.next( sdkChange("swap",e.args.cellID,e.args.candidID));
		return getHandler({args:{value:"cell",cellID:e.args.cellID}});
	case "only":
		//history.unshift( sdkChange("only",[e.args.cellID],[e.args.candidID]));
		//field = history[0].exec( field);
		sdkHistory.next( sdkChange("only",e.args.cellID,e.args.candidID));
		return getHandler({args:{value:"cell",cellID:e.args.cellID}});
}});
intercom.register("calc",function(e){
	if( algors[e.args.value]){
		sdkHistory.next( algors[e.args.value]( sdkHistory.current()));
		return getHandler({args:{value:"field"}});
	}
});
var sdkSlice = Object.extend(function sdkSliceFn( data){
	if( data.isSlice){
		return data;
	}
	if( typeof data == "string" || data instanceof String){
		data = (function parse( sz){
			var field = [];
			for( var i=0; i<sz.length; i++) {
				field.push( sdkCell(
					rules.cellIDs[i],
					sz[i]==="0"? rules.candidIDs :[sz[i]],
					rules.cellsToHouses[ rules.cellIDs[i]]
				));
			}
			return field;
		})( data);
	}	
	return Object.extend(data,{
		isSlice : true,
		isSolved: function(){
			return this.every(function( cell){
				return cell.isSolved();
			});
		},
		disable: function( candidID, bool){
			return sdkSlice( this.map( function(cell){
				return cell.disable(candidID, bool);
			}));
		},
		swap: function( candidID, bool){
			return sdkSlice(this.map( function(cell){
				return cell.swap( candidID, bool);
			}));
		},
		only: function( candidID, bool){
			return sdkSlice(this.map( function(cell){
				return cell.only( candidID, bool);
			}));
		},
		
		// == FILTER ==
		filterBy: function( callback, context){
			return sdkSlice( this.filter(callback, context).concat());
		},
		
		filterByHouse: function( arg){
			return this.filterBy(function (cell) {
					return cell.isInHouse( arg);
				});
		},
		
		filterByCount: function( arg){
			return this.filterBy( function (cell) {
					return cell.count() === arg;
				});
		},
		
		filterNotCount: function( arg){
			return this.filterBy( function (cell) {
					return cell.count() !== arg;
				});
		},
		
		filterByCandid: function( arg){
			return this.filterBy( function (cell) {
					return cell.contains( arg);
				});
		},
		
		filterByID: function( arg){
			return this.filterBy( function( cell){
					return cell.id === arg;
				});
		},
		
		filterNotID: function(arg){
			return this.filterBy( function( cell){
					return cell.id !== arg;
				});
		}
	});
},{ 
	unique: function( arr){
		var newArr = [],
			ids = {};
			
		for( var i= 0; i< arr.length; i++){
			if( arr[i]!==undefined && !ids[ arr[i].id]){
				newArr.push(arr[i]);
				ids[ arr[i].id]=true;
			}
		}
		return sdkSlice( newArr);
	},
	copy: function( arr){
		return sdkSlice(arr.map(function(cell){return sdkCell.copy(cell);}).concat());
	},
	parse: function parse( sz){
		var field = [];
		for( var i=0; i<sz.length; ++i) {
			field.push( sdkCell(
				rules.cellIDs[i],
				sz[i]==="0"? rules.candidIDs :[sz[i]],
				rules.cellsToHouses[ rules.cellIDs[i]]
			));
		}
		return field;
	}
});})({
	name : "classic 8x8x8",
	author: "Kambfhase@hotmail.de",
	version: "0.1",
	mode: "simple",
	candidIDs : ['1','2','3','4','5','6','7','8'],
	cellIDs : [
		'c00','c01','c02','c03','c04','c05','c06','c07','c08',
		'c09','c10','c11','c12','c13','c14','c15','c16','c17',
		'c18','c19','c20','c21','c22','c23','c24','c25','c26',
		'c27','c28','c29','c30','c31','c32','c33','c34','c35',
		'c36','c37','c38','c39','c40','c41','c42','c43','c44',
		'c45','c46','c47','c48','c49','c50','c51','c52','c53',
		'c54','c55','c56','c57','c58','c59','c60','c61','c62',
		'c63'],
	houseIDs : [
		'hr0','hr1','hr2','hr3','hr4','hr5','hr6','hr7',
		'hc0','hc1','hc2','hc3','hc4','hc5','hc6','hc7',
		'hb0','hb1','hb2','hb3','hb4','hb5','hb6','hb7'],
	cellsToHouses: {
		"c00":["hr0","hc0","hb0"],"c01":["hr0","hc1","hb0"],"c02":["hr0","hc2","hb0"],"c03":["hr0","hc3","hb0"],
		"c04":["hr0","hc4","hb1"],"c05":["hr0","hc5","hb1"],"c06":["hr0","hc6","hb1"],"c07":["hr0","hc7","hb1"],
		"c08":["hr1","hc0","hb0"],"c09":["hr1","hc1","hb0"],"c10":["hr1","hc2","hb0"],"c11":["hr1","hc3","hb0"],
		"c12":["hr1","hc4","hb1"],"c13":["hr1","hc5","hb1"],"c14":["hr1","hc6","hb1"],"c15":["hr1","hc7","hb1"],
		"c16":["hr2","hc0","hb2"],"c17":["hr2","hc1","hb2"],"c18":["hr2","hc2","hb2"],"c19":["hr2","hc3","hb2"],
		"c20":["hr2","hc4","hb3"],"c21":["hr2","hc5","hb3"],"c22":["hr2","hc6","hb3"],"c23":["hr2","hc7","hb3"],
		"c24":["hr3","hc0","hb2"],"c25":["hr3","hc1","hb2"],"c26":["hr3","hc2","hb2"],"c27":["hr3","hc3","hb2"],
		"c28":["hr3","hc4","hb3"],"c29":["hr3","hc5","hb3"],"c30":["hr3","hc6","hb3"],"c31":["hr3","hc7","hb3"],
		
		"c32":["hr4","hc0","hb4"],"c33":["hr4","hc1","hb4"],"c34":["hr4","hc2","hb4"],"c35":["hr4","hc3","hb4"],
		"c36":["hr4","hc4","hb5"],"c37":["hr4","hc5","hb5"],"c38":["hr4","hc6","hb5"],"c39":["hr4","hc7","hb5"],
		"c40":["hr5","hc0","hb4"],"c41":["hr5","hc1","hb4"],"c42":["hr5","hc2","hb4"],"c43":["hr5","hc3","hb4"],
		"c44":["hr5","hc4","hb5"],"c45":["hr5","hc5","hb5"],"c46":["hr5","hc6","hb5"],"c47":["hr5","hc7","hb5"],
		
		"c48":["hr6","hc0","hb6"],"c49":["hr6","hc1","hb6"],"c50":["hr6","hc2","hb6"],"c51":["hr6","hc3","hb6"],
		"c52":["hr6","hc4","hb7"],"c53":["hr6","hc5","hb7"],"c54":["hr6","hc6","hb7"],"c55":["hr6","hc7","hb7"],
		"c56":["hr7","hc0","hb6"],"c57":["hr7","hc1","hb6"],"c58":["hr7","hc2","hb6"],"c59":["hr7","hc3","hb6"],
		"c60":["hr7","hc4","hb7"],"c61":["hr7","hc5","hb7"],"c62":["hr7","hc6","hb7"],"c63":["hr7","hc7","hb7"]},
	style:{
		/*'#sdkWrapper': {
			'font-size': '18px' //~450/24
		},*/
		'.cell[houses~="hr2"], .cell[houses~="hr4"], .cell[houses~="hr6"]': {
			"border-top":"black thin solid"
		},
		'.cell[houses~="hc4"]': {
			"border-left":"black thin solid",
			'left':'50%'
		},
		'.cell' : {
			'width':'12.5%',
			'height':'12.5%'
		},
		'.cell[houses~="hc1"]': {
			'left':'12.5%'
		},
		'.cell[houses~="hr1"]': {
			'top':'12.5%'
		},
		'.cell[houses~="hc2"]': {
			'left':'25%'
		},
		'.cell[houses~="hr2"]': {
			'top':'25%'
		},
		'.cell[houses~="hc3"]': {
			'left':'37.5%'
		},
		'.cell[houses~="hr3"]': {
			'top':'37.5%'
		},
		'.cell[houses~="hr4"]': {
			'top':'50%'
		},
		'.cell[houses~="hc5"]': {
			'left':'62.5%'
		},
		'.cell[houses~="hr5"]': {
			'top':'62.5%'
		},
		'.cell[houses~="hc6"]': {
			'left':'75%'
		},
		'.cell[houses~="hr6"]': {
			'top':'75%'
		},
		'.cell[houses~="hc7"]': {
			'left':'87.5%'
		},
		'.cell[houses~="hr7"]': {
			'top':'87.5%'
		},
		'.candid[candidid="1"], .candid[candidid="2"],  .candid[candidid="3"],  .candid[candidid="4"] ': {
			'top' : '0%'
		},
		'.candid[candidid="5"], .candid[candidid="6"],  .candid[candidid="7"],  .candid[candidid="8"] ': {
			'top' : '50%'
		},
		'.candid[candidid="2"], .candid[candidid="6"]': {
			'left': '25%'
		},
		'.candid[candidid="3"], .candid[candidid="7"]': {
			'left': '50%'
		},
		'.candid[candidid="4"], .candid[candidid="8"]': {
			'left': '75%'
		}
	},
	predefined:["0087000024008000000500180000702012300000060004005000074000000001"]
},self)